fix: honor error_handler replacement requests on SessionError and preserve forefront on retries - #2105
Conversation
Honor error_handler replacement requests for SessionError, retire blocked sessions when rotations are exhausted, propagate AdaptivePlaywright static SessionError for rotation instead of browser fallback, and reclaim retries with request.forefront for tiered-proxy priority.
There was a problem hiding this comment.
🟡 Not ready to approve
The SessionError path currently leaves requests in an inconsistent lifecycle state and misses retry/error tracking in one replacement branch, which can lead to incorrect persisted request metadata and statistics.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Pull request overview
This PR completes the SessionError lifecycle in BasicCrawler/AdaptivePlaywrightCrawler and ensures retry reclaiming respects request queue priority (forefront) so tiered-proxy retries stay at the front of the queue.
Changes:
- Honor
error_handlerreturn values forSessionError, wrap handler exceptions consistently, and retire sessions when rotations are exhausted. - Re-raise
SessionErrorfrom the adaptive crawler’s static path to trigger session rotation instead of falling through to the browser. - Pass
forefront=request.forefrontintoreclaim_requestso priority retries preserve queue ordering.
File summaries
| File | Description |
|---|---|
src/crawlee/crawlers/_basic/_basic_crawler.py |
Updates retry reclaim behavior to honor forefront, and refines SessionError handling (rotation/retire + error_handler honoring). |
src/crawlee/crawlers/_adaptive_playwright/_adaptive_playwright_crawler.py |
Ensures static-path SessionError propagates to enable session rotation rather than browser fallback with the same session. |
tests/unit/crawlers/_basic/test_basic_crawler.py |
Adds unit tests covering SessionError error_handler replacement, session retirement on exhausted rotations, and forefront reclaim behavior. |
tests/unit/crawlers/_adaptive_playwright/test_adaptive_playwright_crawler.py |
Adds a unit test asserting static SessionError propagation triggers session rotation and prevents browser fallback. |
Review details
Suppressed comments (1)
src/crawlee/crawlers/_basic/_basic_crawler.py:1504
- When session rotations are exhausted, the request is marked as handled without setting its final state to ERROR. This leaves failed requests in REQUEST_HANDLER state in storage, which diverges from the normal error path (where the request is set to RequestState.ERROR before marking handled).
else:
# Exhausted rotations: retire the blocked session so it is not reused from the pool.
session.retire()
await self._mark_request_as_handled(request)
- Files reviewed: 4/4 changed files
- Comments generated: 2
- Review effort level: Lite
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
Mantisus
left a comment
There was a problem hiding this comment.
Hey, @Ayush7614. Thank you for your contribution!
Honor error_handler replacements only while rotations remain, keep session.retire() on the retry path only, restore AdaptivePlaywright static SessionError fallback to browser, and use patch.object for forefront reclaim coverage.
|
Thanks for the review @Mantisus! Addressed in
|
Mantisus
left a comment
There was a problem hiding this comment.
One nit, and please update the PR description to match the current state. Otherwise LGTM, thanks for contributing!
Assertions raised inside error_handler are wrapped as UserDefinedErrorHandlerError, so remove the isinstance check.
|
Thanks @Mantisus — addressed in
|
Pydantic field-wise request inequality made same-URL replacements mark the in-progress original handled while add_request was a no-op. Share the replacement path, inherit retry counters, finish stats, and retire blocked sessions before awaits.
|
Thanks @vdusek Addressed in a138011:
|
error_handler replacement requests on SessionError and preserve forefront on retries
error_handler replacement requests on SessionError and preserve forefront on retries
Summary
SessionErrorpath: honorerror_handlerreplacement requests only while session rotations remain; treat sameunique_keyas no replacement (avoids marking the in-progress request handled whileadd_requestis a no-op); wrap handler exceptions inUserDefinedErrorHandlerErrorand setRequestState.ERRORwhen the handler raises or rotations are exhausted._handle_error_handler_replacementhelper for the SessionError and normal retry paths: inheritsretry_count/session_rotation_count, passesforefront=toadd_request, recordsrecord_request_processing_finishon the replaced original, and retires blocked sessions before awaits on the SessionError path.reclaim_requestpassesforefront=request.forefrontso tiered-proxy priority retries stay at the front of the queue.session.retire()stays on the retry/rotation path only (preserves long-lived sessions whenmax_session_rotations=0).Why
error_handlercould replace a request on normal failures, but its return value was discarded forSessionError. A field-wisenew_request != requestcheck treated same-URL replacements as new work and could drop the original. Tiered proxies setrequest.forefront = Trueon retry, but reclaim always used the defaultforefront=False.Test plan
test_session_error_handler_can_replace_requesttest_session_error_handler_same_unique_key_rotatestest_session_error_handler_replacement_ignored_when_rotations_exhaustedtest_session_error_handler_exception_is_wrappedtest_reclaim_uses_request_forefront_flag